package com.group.app.example.common.support;


import com.fasterxml.jackson.databind.ObjectMapper;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.SerializationException;
import org.hibernate.usertype.UserType;
import org.postgresql.util.PGobject;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
import  org.slf4j.Logger;

public class JsonbType implements UserType {

    private Logger LOG= LoggerFactory.getLogger(this.getClass());

    private final ObjectMapper mapper = new ObjectMapper();
    @Override
    public int[] sqlTypes() {
        return new int[]{Types.JAVA_OBJECT};
    }

    @Override
    public Class returnedClass() {
        return Map.class;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return ObjectUtils.nullSafeEquals(x,y);
    }

    @Override
    public int hashCode(Object o) throws HibernateException {
        if (o == null){
            return 0;
        }
        return o.hashCode();

    }

    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor sharedSessionContractImplementor, Object owner) throws HibernateException, SQLException {
        PGobject o=(PGobject)resultSet.getObject(names[0]);
        if(o.getValue() != null){
            try {
               return mapper.readValue(o.getValue(),Map.class);
            }catch (IOException e){
                LOG.error(e.getMessage());
            }
        }
        return new HashMap<String,Object>();
    }

    @Override
    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SharedSessionContractImplementor sharedSessionContractImplementor) throws HibernateException, SQLException {
         if (value ==null){
             preparedStatement.setNull(index,Types.OTHER);
         }else{
             try {
                 preparedStatement.setObject(index,mapper.writeValueAsString(value),Types.OTHER);
             }catch (IOException e){
                 LOG.error(e.getMessage());
             }
         }
    }

    @Override
    public Object deepCopy(Object o) throws HibernateException {
        if (o !=null){
            try {
                return mapper.readValue(mapper.writeValueAsString(o),returnedClass());
            }catch (IOException e){
                throw new HibernateException("Failed to deep copy object",e);
            }
        }
        return null;
    }

    @Override
    public boolean isMutable() {
        return true;
    }

    @Override
    public Serializable disassemble(Object o) throws HibernateException {
        Object copy = deepCopy(o);
        if (copy instanceof Serializable){
            return (Serializable) copy;
        }
        throw new SerializationException(String.format("Cannot serialize '%s', %s is not Serializable",o,o.getClass()),null);
    }

    @Override
    public Object assemble(Serializable serializable, Object o) throws HibernateException {
        return deepCopy(serializable);
    }

    @Override
    public Object replace(Object original, Object target, Object owner) throws HibernateException {
        return deepCopy(original);
    }
}
